// Package database
// Title 			mysql.database.components.go
// Description 		MySQL 数据库工厂, 实现 factory.ApplicationDatabaseLoaderFactory, 实现其远程加载器接口方法
// Author  			alittlexincan@163.com 					2024/07/22 11:36
// Update  			alittlexincan@163.com 					2024/07/22 11:36
package database

import (
	"fmt"
	"log"
	"time"

	"github.com/bwmarrin/snowflake"
	"go.uber.org/zap"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"xincan.com.cn/tarot-go/internal/common/constants"
	"xincan.com.cn/tarot-go/internal/config/factory"
)

// mysqlConfigComponent
// Title mysqlConfigComponent
// Description  		mysql 配置信息
// Author  				alittlexincan@163.com 			2024/07/22 11:36
// Update  				alittlexincan@163.com 			2024/07/22 11:36
type mysqlConfigComponent struct {
	factory.BaseConfigLoader[factory.ApplicationDatabaseLoaderFactory]                 // 基础配置加载器, 内置全局配置和日志
	orm                                                                *gorm.Config    // ORM 配置
	node                                                               *snowflake.Node // 雪花算法
	Host                                                               string          `mapstructure:"host" json:"host"`                                           // 数据库地址
	Port                                                               string          `mapstructure:"port" json:"port"`                                           // 数据库端口
	User                                                               string          `mapstructure:"user" json:"user"`                                           // 数据库用户
	Password                                                           string          `mapstructure:"password" json:"password"`                                   // 数据库密码
	Database                                                           string          `mapstructure:"database" json:"database"`                                   // 数据库名称
	Charset                                                            string          `mapstructure:"charset" json:"charset"`                                     // 编码设置: 支持完整的UTF-8编码,需设置成: utf8mb4, 默认: utf8mb4
	ParseTime                                                          bool            `mapstructure:"parseTime" json:"parseTime"`                                 // 是否解析时间类型, 默认: true
	TimeZone                                                           string          `mapstructure:"timeZone" json:"timeZone"`                                   // 时区设置: 若设置 Asia/Shanghai,需写成 Asia%2fShanghai, 默认: Asia%2fShanghai
	DefaultStringSize                                                  int             `mapstructure:"defaultStringSize" json:"defaultStringSize"`                 // string 类型字段的默认长度
	DisableDatetimePrecision                                           bool            `mapstructure:"disableDatetimePrecision" json:"disableDatetimePrecision"`   // 是否开启禁用时间精度: MySQL 5.6 之前的数据库不支持, 默认: true
	SkipInitializeWithVersion                                          bool            `mapstructure:"skipInitializeWithVersion" json:"skipInitializeWithVersion"` // 根据当前 MySQL 版本自动配置
	AutoMigrate                                                        bool            `mapstructure:"autoMigrate" json:"autoMigrate"`                             // 是否开启更新表结构: 每次服务启动都会根据实体创建/更新表结构, 默认: true
	MigrateModels                                                      []interface{}   `mapstructure:"migrateModels" json:"migrateModels"`                         // 迁移模型, 将要迁移的结构体写入模型数组中
	SlowSql                                                            string          `mapstructure:"slowSql" json:"slowSql"`                                     // 慢查询设置: 单位毫秒, 默认: 50ms
	LogPath                                                            string          `mapstructure:"logPath" json:"logPath"`                                     // 日志存放地址,默认: 采用 service.log.path + "/" + tarot.App.Service.Name + "/mysql/sql.log"
	LogLevel                                                           string          `mapstructure:"logLevel" json:"logLevel"`                                   // 日志级别: error、info、warn, 默认: info
	IgnoreRecordNotFoundError                                          bool            `mapstructure:"ignoreRecordNotFoundError" json:"ignoreRecordNotFoundError"` // 是否忽略ErrRecordNotFound(未查到记录错误), 默认: true
	MaxIdleConns                                                       int             `mapstructure:"maxIdleConns" json:"maxIdleConns"`                           // 设置最大空闲连接数, 默认: 10
	MaxOpenConns                                                       int             `mapstructure:"maxOpenConns" json:"maxOpenConns"`                           // 设置最大打开连接数, 默认: 100
	ConnMaxLifetime                                                    time.Duration   `mapstructure:"connMaxLifetime" json:"connMaxLifetime"`                     // 设置连接最大生命周期, 默认: 1h
}

// WithOrmConfig
// Title 			WithOrmConfig
// Description 		实现 factory.ApplicationDatabaseLoaderFactory 接口方法, 获取MySQL ORM配置信息
// Author  			alittlexincan@163.com 								2024/07/22 11:36
// Update  			alittlexincan@163.com 								2024/07/22 11:36
// Param    		ormConfig *gorm.Config 								返回ORM配置
// Return    		factory.ApplicationDatabaseLoaderFactory    		返回数据库配置工厂实例
func (db *mysqlConfigComponent) WithOrmConfig(ormConfig *gorm.Config) factory.ApplicationDatabaseLoaderFactory {
	db.orm = ormConfig
	return db
}

// WithSnowflake
// Title 			WithSnowflake
// Description 		实现 factory.ApplicationDatabaseLoaderFactory 接口方法, 获取MySQL雪花ID配置信息
// Author  			alittlexincan@163.com 								2024/07/22 11:36
// Update  			alittlexincan@163.com 								2024/07/22 11:36
// Param    		snowflake *snowflake.Node 							返回雪花ID配置信息
// Return    		factory.ApplicationDatabaseLoaderFactory    		返回数据库配置工厂实例
func (db *mysqlConfigComponent) WithSnowflake(snowflake *snowflake.Node) factory.ApplicationDatabaseLoaderFactory {
	db.node = snowflake
	return db
}

// WithMigrateModel
// Title 			WithMigrateModel
// Description 		实现 factory.ApplicationDatabaseLoaderFactory 接口方法, 获取MySQL迁移模型
// Author  			alittlexincan@163.com 								2024/07/22 11:36
// Param    		model interface{} 									返回迁移模型
// Return    		factory.ApplicationDatabaseLoaderFactory    		返回数据库配置工厂实例
func (db *mysqlConfigComponent) WithMigrateModel(model interface{}) factory.ApplicationDatabaseLoaderFactory {
	db.MigrateModels = append(db.MigrateModels, model)
	return db
}

// ConfigLoad
// Title 			ConfigLoad
// Description 		实现 factory.ApplicationDatabaseLoaderFactory 接口方法, 获取MySQL数据库配置信息
// Author  			alittlexincan@163.com 								2024/07/22 11:36
// Update  			alittlexincan@163.com 								2024/07/22 11:36
// Return    		*gorm.DB    										返回数据库实例
// Return    		factory.ApplicationDatabaseLoaderFactory    		返回数据库配置工厂实例
func (db *mysqlConfigComponent) ConfigLoad() (*gorm.DB, factory.ApplicationDatabaseLoaderFactory) {
	// 1: 检查数据库信息配置
	if db.Host == "" || db.Port == "" || db.Database == "" {
		db.Logger.Error("加载存储信息配置: 错误, 请检查数据库信息配置")
	}
	// 2: 创建mysql dsn
	dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=%s&parseTime=%t&loc=%s", db.User, db.Password, db.Host, db.Port, db.Database, db.Charset, db.ParseTime, db.TimeZone)
	// 3: 基于gorm, 创建数据库连接
	client, err := gorm.Open(mysql.Open(dsn), db.orm)
	if err != nil {
		db.Logger.Error("加载存储信息配置: 客户端连接失败", zap.String("dsn", dsn), zap.Error(err))
	}
	// 4: 配置连接池
	sqlDB, err := client.DB()
	if err != nil {
		db.Logger.Error("加载存储信息配置: 配置链接池失败", zap.String("dsn", dsn), zap.Error(err))
	}
	sqlDB.SetMaxIdleConns(db.MaxIdleConns)       // 最大空闲连接
	sqlDB.SetMaxOpenConns(db.MaxOpenConns)       // 最大打开连接
	sqlDB.SetConnMaxLifetime(db.ConnMaxLifetime) // 连接最大生命周期

	// 5: 设置自动补充Id
	db.autoId(client)

	// 6: 是否调用数据迁移
	db.autoMigrate(client)

	db.Logger.Info("加载信息配置成功: 客户端连接成功", zap.String("dsn", dsn))
	return client, db
}

func (db *mysqlConfigComponent) autoId(client *gorm.DB) {
	// 自动设置Id
	// 判断Id字段类型是否是自定义类型, 如果是自定义类型且是主键 ,并且为空, 则生成雪花Id, 否则不生成
	err := client.Callback().Create().Before("gorm:create").Register("assign_snowflake_id", func(tx *gorm.DB) {
		// 获取当前操作的模型
		model := tx.Statement.Schema
		if model == nil {
			return
		}
		// 遍历模型的所有字段
		for _, field := range model.Fields {
			// 检查字段是否为主键
			if field.PrimaryKey {
				// 检查字段值是否为空（零值）
				if _, isZero := field.ValueOf(tx.Statement.Context, tx.Statement.ReflectValue); isZero {
					// 确保字段类型为 int64 或 uint64，或者您期望的类型
					// 这里假设主键字段类型为 int64
					if field.DataType == "int64" { // 根据实际情况调整
						// 生成 Snowflake ID
						snowflakeID := db.node.Generate().Int64()
						// 设置字段值为 Snowflake ID
						if err := field.Set(tx.Statement.Context, tx.Statement.ReflectValue, snowflakeID); err != nil {
							db.Logger.Error("自动设置 Snowflake ID 失败", zap.String("field", field.Name), zap.Error(err))
						} else {
							db.Logger.Debug("自动设置 Snowflake ID 成功", zap.String("field", field.Name), zap.Int64("ID", snowflakeID))
						}
					}
					// 如果字段类型不是 int64，您可以根据需要添加更多类型判断
				}
			}
		}
	})
	if err != nil {
		log.Printf("MySQL配置: 自动设置雪花ID失败, err: %v", err)
	}
}

func (db *mysqlConfigComponent) autoMigrate(client *gorm.DB) {
	if db.AutoMigrate {
		if len(db.MigrateModels) == 0 {
			db.Logger.Warn("存储迁移信息配置: 自动迁移已启用，但未配置迁移模型")
		} else {
			if err := client.Set("gorm:table_options", "ENGINE=InnoDB").AutoMigrate(db.MigrateModels...); err != nil {
				db.Logger.Error("存储迁移信息配置: 模型迁移失败", zap.Error(err))
			}
			db.Logger.Info("存储迁移信息成功: 模型迁移成功", zap.Int("count", len(db.MigrateModels)))
		}
	}
}

// init
// Title 			init
// Description 		初始化时自动实例化自身实例, 并在main包中导入当前包路径
// Author      		alittlexincan@163.com       			2024/07/17 11:36
// Update    		alittlexincan@163.com       			2024/07/17 11:36
func init() {
	factory.DatabaseCenterFactory(constants.ProviderDatabaseMysql, &mysqlConfigComponent{})
}
